 /*******************************************************************************
  * Copyright (c) 2006, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal;

 import java.util.ArrayList ;
 import java.util.Iterator ;
 import java.util.List ;

 import org.eclipse.swt.dnd.DND;
 import org.eclipse.swt.dnd.DropTarget;
 import org.eclipse.swt.dnd.DropTargetEvent;
 import org.eclipse.swt.dnd.DropTargetListener;
 import org.eclipse.swt.dnd.Transfer;
 import org.eclipse.swt.dnd.TransferData;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.dnd.IDragAndDropService;
 import org.eclipse.ui.services.IDisposable;

 /**
  * Implementation for the <code>IDragAndDropService</code> to be used from
  * <code>EditorSite</code>'s.
  * </p><p>
  * Adds a drop target to the given control that merges the site's
  * drop behaviour with that specified by the <code>addMergedDropTarget</code> call.
  * </p><p>
  * The current implementation is only defined for EditorSite's and merges the
  * given drop handling with the existing EditorSashContainer's behaviour.
  * </p><p>
  * NOTE: There is no cleanup (i.e. 'dispose') handling necessary for merged
  * Drop Targets but the hooks are put into place to maintain the consistency
  * of the implementation pattern.
  * </p>
  * @since 3.3
  *
  */
 public class EditorSiteDragAndDropServiceImpl implements IDragAndDropService, IDisposable {
     /**
      * Implementation of a DropTarget wrapper that will either delegate to the
      * <code>primaryListener</code> if the event's <code>currentDataType</code>
      * can be handled by it; otherwise the event is forwarded on to the
      * listener specified by <code>secondaryListener</code>.
      * </p><p>
      * NOTE: we should perhaps refactor this out into an external class
      * </p>
      * @since 3.3
      *
      */
     private static class MergedDropTarget {
         private DropTarget realDropTarget;
         
         private Transfer[] secondaryTransfers;
         private DropTargetListener secondaryListener;
         
         private Transfer[] primaryTransfers;
         private DropTargetListener primaryListener;
         
         public MergedDropTarget(Control control,
                 int priOps, Transfer[] priTransfers, DropTargetListener priListener,
                 int secOps, Transfer[] secTransfers, DropTargetListener secListener) {
             realDropTarget = new DropTarget(control, priOps | secOps);
             
             // Cache the editor's transfers and listener
 primaryTransfers = priTransfers;
             primaryListener = priListener;
             
             // Capture the editor area's current transfers & listener
 WorkbenchWindow ww = (WorkbenchWindow) PlatformUI.getWorkbench().getActiveWorkbenchWindow();
             WorkbenchWindowConfigurer winConfigurer = ww.getWindowConfigurer();
             secondaryTransfers = winConfigurer.getTransfers();
             secondaryListener = winConfigurer.getDropTargetListener();
             
             // Combine the two sets of transfers into one array
 Transfer[] allTransfers = new Transfer[secondaryTransfers.length+primaryTransfers.length];
             int curTransfer = 0;
             for (int i = 0; i < primaryTransfers.length; i++) {
                 allTransfers[curTransfer++] = primaryTransfers[i];
             }
             for (int i = 0; i < secondaryTransfers.length; i++) {
                 allTransfers[curTransfer++] = secondaryTransfers[i];
             }
             realDropTarget.setTransfer(allTransfers);
             
             // Create a listener that will delegate to the appropriate listener
 // NOTE: the -editor- wins (i.e. it can over-ride WB behaviour if it wants
 realDropTarget.addDropListener(new DropTargetListener() {
                 public void dragEnter(DropTargetEvent event) {
                     getAppropriateListener(event).dragEnter(event);
                 }
                 public void dragLeave(DropTargetEvent event) {
                     getAppropriateListener(event).dragLeave(event);
                 }
                 public void dragOperationChanged(DropTargetEvent event) {
                     getAppropriateListener(event).dragOperationChanged(event);
                 }
                 public void dragOver(DropTargetEvent event) {
                     getAppropriateListener(event).dragOver(event);
                 }
                 public void drop(DropTargetEvent event) {
                     getAppropriateListener(event).drop(event);
                 }
                 public void dropAccept(DropTargetEvent event) {
                     getAppropriateListener(event).dropAccept(event);
                 }
             });
         }

         private DropTargetListener getAppropriateListener(DropTargetEvent event) {
             if (isSupportedType(primaryTransfers, event.currentDataType))
                 return primaryListener;
             
             return secondaryListener;
         }
         
         private boolean isSupportedType(Transfer[] transfers, TransferData transferType) {
             for (int i = 0; i < transfers.length; i++) {
                 if (transfers[i].isSupportedType(transferType))
                     return true;
             }
             return false;
         }

         /**
          * Clean up...
          */
         public void dispose() {
         }
     }
     
     // Cache any listeners for cleanup
 List addedListeners = new ArrayList ();

     /* (non-Javadoc)
      * @see org.eclipse.ui.dnd.IEditorDropTargetService#addDropTarget(org.eclipse.swt.widgets.Control, int, org.eclipse.swt.dnd.Transfer[], org.eclipse.swt.dnd.DropTargetListener)
      */
     public void addMergedDropTarget(Control control, int ops, Transfer[] transfers,
             DropTargetListener listener) {
          // First we have to remove any existing drop target from the control
 removeMergedDropTarget(control);
         
         // Capture the editor area's current ops, transfers & listener
 int editorSiteOps = DND.DROP_DEFAULT | DND.DROP_COPY | DND.DROP_LINK;

         WorkbenchWindow ww = (WorkbenchWindow) PlatformUI.getWorkbench().getActiveWorkbenchWindow();
         WorkbenchWindowConfigurer winConfigurer = ww.getWindowConfigurer();
         Transfer[] editorSiteTransfers = winConfigurer.getTransfers();
         DropTargetListener editorSiteListener = winConfigurer.getDropTargetListener();
         
         // Create a new 'merged' drop Listener using combination of the desired
 // transfers and the ones used by the EditorArea
 MergedDropTarget newTarget = new MergedDropTarget(control, ops, transfers, listener,
                 editorSiteOps, editorSiteTransfers, editorSiteListener);
         addedListeners.add(newTarget);
     }

     /**
      * This method will return the current drop target for the control
      * (whether or not it was created using this service.
      * <p>
      * <b>WARNING:</b> This code uses an SWT internal string to gain
      * access to the drop target. I've been assured that neither the
      * value of the string nor the fact that the target is stored in
      * a property will change for 3.3 and that post-3.3 we will come
      * up with a more viable DnD SWT story...
      * </p>
      * @param control The control to get the drop target for
      * @return The DropTarget for that control (could be null
      */
     private DropTarget getCurrentDropTarget(Control control) {
         if (control == null)
             return null;
         
         Object curDT = control.getData("DropTarget"); //$NON-NLS-1$
 return (DropTarget)curDT;
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.ui.dnd.IDragAndDropService#removeMergedDropTarget(org.eclipse.swt.widgets.Control)
      */
     public void removeMergedDropTarget(Control control) {
         DropTarget targetForControl = getCurrentDropTarget(control);
         if (targetForControl != null) {
             targetForControl.dispose();
             addedListeners.remove(targetForControl);
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.services.IDisposable#dispose()
      */
     public void dispose() {
         // Clean up the listeners
 for (Iterator iterator = addedListeners.iterator(); iterator.hasNext();) {
             MergedDropTarget target = (MergedDropTarget) iterator.next();
             target.dispose();
         }
         addedListeners.clear();
     }

 }

